package com.fgxia.admin.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.fgxia.admin.service.IAdminWaybillService;
import com.ruoyi.business.domain.*;
import com.ruoyi.business.domain.bo.OrderFeeBo;
import com.ruoyi.business.domain.bo.admin.*;
import com.ruoyi.business.domain.vo.*;
import com.ruoyi.business.domain.vo.admin.*;
import com.ruoyi.business.mapper.*;
import com.ruoyi.business.utils.OrderUtils;
import com.ruoyi.common.constant.PushTemplate;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.event.PushMessageEvent;
import com.ruoyi.common.core.domain.event.WarningEvent;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.*;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.exception.user.UserException;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.common.utils.BeanCopyUtils;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * 运单服务实现
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class AdminWaybillServiceImpl implements IAdminWaybillService {

    private final FreightVehicleTrailMapper freightVehicleTrailMapper;

    private final FreightInfoMapper freightInfoMapper;
    private final OrderInfoMapper orderInfoMapper;
    private final OrderFeeMapper orderFeeMapper;

    private final FreightRevaluationHistoryMapper freightRevaluationHistoryMapper;
    private final FreightReceiptMapper freightReceiptMapper;
    private final FreightPaymentVoucherMapper paymentVoucherMapper;

    private final SysUserLoginDriverMapper driverMapper;
    private final SysUserMapper userMapper;

    /**
     * 查询运单详情
     *
     * @param orderId
     * @return
     */
    @Override
    public AdminWaybillAddVo selectWaybillById(Long orderId) {
        AdminWaybillAddVo waybillAddVo = new AdminWaybillAddVo();
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        if (orderInfo == null) {
            throw new ServiceException("订单不存在");
        }
        waybillAddVo.setOrderId(orderInfo.getOrderId());
        waybillAddVo.setOrderNo(orderInfo.getOrderNo());
        List<OrderFeeVo> orderFeeVos = orderFeeMapper.selectVoListByMasterId(orderInfo.getOrderId());
        waybillAddVo.setOrderFeeList(orderFeeVos);
        List<FreightInfo> freightInfoList = freightInfoMapper.selectByOrderId(orderInfo.getOrderId());
        List<AdminWayBillFeeVo> wayBillFeeVoList = BeanCopyUtils.copyList(freightInfoList, AdminWayBillFeeVo.class);
        waybillAddVo.setWayBillFeeList(wayBillFeeVoList);
        return waybillAddVo;
    }

    /**
     * 查询运单详情(详情用)
     *
     * @param waybillId
     * @return
     */
    @Override
    public AdminWaybillQueryVo queryWaybillById(Long waybillId) {
        QueryWrapper queryWrapper = new QueryWrapper<>();
        queryWrapper
            .eq("f.freight_id", waybillId);

        AdminWaybillQueryVo waybillQueryVo = freightInfoMapper.selectAdminQueryVo(queryWrapper);
        if (waybillQueryVo == null) {
            throw new ServiceException("运单不存在");
        }
        // 打款信息
        FreightPaymentVoucherVo freightPaymentVoucherVo = paymentVoucherMapper.selectVoByFreightId(waybillId);
        waybillQueryVo.setFreightPaymentVoucherVo(freightPaymentVoucherVo);
        // 最新定位
        FreightVehicleTrailVo freightVehicleTrailVo = freightVehicleTrailMapper.selectLatestTrailByMasterId(waybillId);
        waybillQueryVo.setLatestPosition(freightVehicleTrailVo);
        // 回单信息
        FreightReceiptVo freightReceiptVo = freightReceiptMapper.selectVoByFreightId(waybillId);
        waybillQueryVo.setFreightReceiptVo(freightReceiptVo);

        return waybillQueryVo;
    }

    /**
     * 新增运单
     *
     * @param waybill 运单新增业务对象
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Boolean insertWaybill(AdminWaybillAddBo waybill) {
        Long orderId = waybill.getOrderId();
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        if (orderInfo == null) {
            throw new ServiceException("订单不存在");
        }

        // 保存运费信息
        orderFeeMapper.deleteByMasterId(orderId);

        List<OrderFeeBo> orderFeeList = waybill.getOrderFeeList();
        List<OrderFee> orderFees = BeanCopyUtils.copyList(orderFeeList, OrderFee.class);
        if (orderFees == null) {
            orderFees = new ArrayList<>();
        } else {
            orderFees.forEach(orderFee -> {
                orderFee.setOrderId(orderId);
            });
            orderFeeMapper.insertBatch(orderFees);
        }

        // 算出订单总费用
        BigDecimal orderTotalAmount = orderFees.stream()
            .map(OrderFee::getAmount)
            .filter(Objects::nonNull)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
        orderInfo.setOrderTotalPrices(orderTotalAmount);

        int updated = orderInfoMapper.update(new LambdaUpdateWrapper<OrderInfo>()
            .eq(OrderInfo::getOrderId, orderId)
            .set(OrderInfo::getOrderTotalPrices, orderTotalAmount));


        // 保存运单信息
        List<AdminWayBillFeeBo> wayBillFeeList = waybill.getWayBillFeeList();

        // 司机不能重复
        if (wayBillFeeList.stream().map(AdminWayBillFeeBo::getDriverTel).distinct().count()
            != wayBillFeeList.size()) {
            throw new ServiceException("一个司机不能同时承担多次运输");
        }

        List<FreightInfo> freightInfoList = new ArrayList<>();
        for (AdminWayBillFeeBo adminWayBillFeeBo : wayBillFeeList) {
            // 判断司机是否承担多次运输
            long countNum = freightInfoMapper.countByDriverTel(adminWayBillFeeBo.getDriverTel(),
                CollUtil.newArrayList(FreightStatus.WAITING_FOR_PICKUP.getCode(), FreightStatus.DELIVERING.getCode()));
            if (countNum > 0) {
                throw new ServiceException(StrUtil.format("司机{}正在承担运输任务,无法重复指派", adminWayBillFeeBo.getDriverTel(), countNum));
            }

            FreightInfo freightInfo = new FreightInfo();
            freightInfo.setOrderId(orderId);
            freightInfo.setOrderNo(orderInfo.getOrderNo());
            freightInfo.setFreightStatus(FreightStatus.WAITING_FOR_PICKUP.getCode());


            // 手机号反查，反查不到就创建
            SysUserLoginDriver sysUserLoginDriver = driverMapper.selectByPhone(adminWayBillFeeBo.getDriverTel());
            if (sysUserLoginDriver == null) {
                sysUserLoginDriver = this.insertDriver(adminWayBillFeeBo);
            }
            freightInfo.setCode(OrderUtils.generateWaybillNo(orderInfo.getRelevanceUserId()));
            freightInfo.setRelevanceUserId(sysUserLoginDriver.getRelevanceUserId());
            freightInfo.setDriverName(adminWayBillFeeBo.getDriverName());
            freightInfo.setDriverTel(adminWayBillFeeBo.getDriverTel());
            freightInfo.setDriverType(adminWayBillFeeBo.getDriverType());
            freightInfo.setFreightAmount(adminWayBillFeeBo.getFreightAmount());
            freightInfo.setFreightType(orderInfo.getCarUseType());
            freightInfo.setSecondLevelAddress(adminWayBillFeeBo.getSecondLevelAddress());
            freightInfo.setSecondLevelLongitude(adminWayBillFeeBo.getSecondLevelLongitude());
            freightInfo.setSecondLevelLatitude(adminWayBillFeeBo.getSecondLevelLatitude());
            freightInfo.setSecondLevelLinkman(adminWayBillFeeBo.getSecondLevelLinkman());
            freightInfo.setSecondLevelTel(adminWayBillFeeBo.getSecondLevelTel());

            freightInfoList.add(freightInfo);
        }

        // 保存运单信息
        boolean insertBatch = freightInfoMapper.insertBatch(freightInfoList);

        int update = orderInfoMapper.update(new LambdaUpdateWrapper<OrderInfo>()
            .eq(OrderInfo::getOrderId, orderId)
            .set(OrderInfo::getOrderStatus, OrderStatus.WAITING_FOR_PICKUP.getCode()));

        return insertBatch && update > 0;
    }

    /**
     * 新增司机
     *
     * @param adminWayBillFeeBo
     * @return
     */
    private SysUserLoginDriver insertDriver(AdminWayBillFeeBo adminWayBillFeeBo) {
        String phonenumber = adminWayBillFeeBo.getDriverTel();
        SysUser user = userMapper.selectUserByPhonenumber(phonenumber);

        if (user == null) {
            user = new SysUser();
            user.setNickName("司机" + RandomUtil.randomString(8));
            user.setPhonenumber(phonenumber);
            user.setUserName(phonenumber);
            user.setStatus(UserStatus.OK.getCode());
            user.setCreateBy(LoginHelper.getUsername());
            user.setCreateTime(DateUtils.getNowDate());
            // user.setPassword(BCrypt.hashpw(user.getPassword()));
            user.setPassword(null);

            int insert = userMapper.insert(user);
            if (insert == 0) {
                log.info("司机用户：{} 注册失败.", phonenumber);
                throw new UserException("司机用户注册失败");
            }
        }

        SysUserLoginDriver driver = new SysUserLoginDriver();
        driver.setRealName(adminWayBillFeeBo.getDriverName());
        driver.setUserType(UserType.APP_DRIVER.getUserType());
        driver.setDriverType(adminWayBillFeeBo.getDriverType());
        driver.setRelevanceUserId(user.getUserId());
        int insert = driverMapper.insert(driver);
        if (insert == 0) {
            throw new ServiceException("司机创建失败");
        }

        return driver;
    }

    /**
     * 更新运单
     *
     * @param waybill
     * @return
     */
    @Override
    public Boolean updateWaybill(AdminWaybillAddBo waybill) {
        // 配送中就不能改了

        return null;
    }

    /**
     * 定位打卡
     *
     * @param location
     * @return
     */
    @Override
    public Boolean insertLocation(AdminLocationAddBo location) {
        Long id = location.getMasterId();
        FreightInfo freightInfo = freightInfoMapper.selectById(id);
        if (freightInfo == null) {
            throw new ServiceException("运单不存在");
        }
        // 修改运单紧急状态，只有在配送中
        if (!FreightStatus.DELIVERING.getCode().equals(freightInfo.getFreightStatus())) {
            throw new ServiceException("只有在配送中的运单可以定位打卡");
        }

        FreightVehicleTrail freightVehicleTrail = new FreightVehicleTrail();
        freightVehicleTrail.setFreightId(location.getMasterId());
        freightVehicleTrail.setLng(location.getLongitude());
        freightVehicleTrail.setLat(location.getLatitude());
        freightVehicleTrail.setTrail(location.getTrail());
        freightVehicleTrail.setIsBlue(SysYesNo.Y.name());
        freightVehicleTrail.setRelevanceUserId(LoginHelper.getUserId());
        freightVehicleTrail.setRecordTime(location.getTrailTime());
        freightVehicleTrail.setIsStartPoint(SysYesNo.N.name());
        freightVehicleTrail.setIsEndPoint(SysYesNo.N.name());
        freightVehicleTrail.setIsOperationAdd(SysYesNo.Y.name());
        int insert = freightVehicleTrailMapper.insert(freightVehicleTrail);

        return insert > 0;
    }

    /**
     * 更新运单价格
     *
     * @param price
     * @return
     */
    @Override
    public Boolean updateWaybillPrice(AdminPriceEditBo price) {

        Long id = price.getMasterId();
        FreightInfo freightInfo = freightInfoMapper.selectById(id);
        if (freightInfo == null) {
            throw new ServiceException("运单不存在");
        }
        FreightRevaluationHistory freightRevaluationHistory = new FreightRevaluationHistory();
        freightRevaluationHistory.setFreightId(id);
        freightRevaluationHistory.setFreightOperateUser(LoginHelper.getUserId());
        freightRevaluationHistory.setBeforeAmount(freightInfo.getFreightAmount());
        freightRevaluationHistory.setAfterAmount(price.getPrice());
        freightRevaluationHistory.setRemark(price.getRemark());
        int insert = freightRevaluationHistoryMapper.insert(freightRevaluationHistory);
        if (insert < 0) {
            return false;
        }
        // 修改运单费用
        int update = freightInfoMapper.update(new LambdaUpdateWrapper<FreightInfo>()
            .eq(FreightInfo::getFreightId, id)
            .set(FreightInfo::getFreightAmount, price.getPrice()));
        if (update == 0) {
            throw new ServiceException("修改运单费用失败");
        }

        // 进行推送
        // 推送给司机
        SpringUtils.context().publishEvent(PushMessageEvent.builder()
            .pushRef(PushRefType.WAYBILL_CHANGE_PRICE.getValue())
            .pushTo(PushMessageEvent.pushAll(freightInfo.getDriverTel()))
            .pushTime(new Date())
            .pushContent(PushTemplate.WAYBILL_CHANGE_PRICE_TEMPLATE(freightInfo.getOrderNo(), price.getPrice(), price.getRemark()))
            .build());
        // 推送给配置表中的人员

        // 推送给配置表中人员
        SpringUtils.context().publishEvent(WarningEvent.builder()
            .ownerId(freightInfo.getFreightId())
            .ownerType("freight")
            .type(PushRefType.WAYBILL_CHANGE_PRICE.getValue())
            .content(PushTemplate.WAYBILL_CHANGE_PRICE_TEMPLATE(freightInfo.getOrderNo(), price.getPrice(), price.getRemark()))
            .build());


        return true;
    }

    /**
     * 紧急运单
     *
     * @param urgent
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean urgentWaybill(AdminUrgentBo urgent) {
        Long id = urgent.getId();
        FreightInfo freightInfo = freightInfoMapper.selectById(id);
        if (freightInfo == null) {
            throw new ServiceException("运单不存在");
        }
        // 修改运单紧急状态，只有在配送中
        if (!FreightStatus.DELIVERING.getCode().equals(freightInfo.getFreightStatus())) {
            throw new ServiceException("只有在配送中的运单可以设置为紧急状态");
        }
        // 运单在紧急状态下才能取消
        if (!SysYesNo.N.name().equals(freightInfo.getIsUrgency())) {
            throw new ServiceException("运单已在紧急状态下");
        }

        int update = freightInfoMapper.updateUrgent(id, SysYesNo.Y.name(), urgent.getRemark());
        if (update == 0) {
            return false;
        }

        // 如果运单进入了紧急状态，那么把订单也进入紧急状态
        int updateOrderStatus = orderInfoMapper.updateUrgent(freightInfo.getOrderId(), SysYesNo.Y.name(), urgent.getRemark());

        // 推送给配置表中人员
        SpringUtils.context().publishEvent(WarningEvent.builder()
            .ownerId(freightInfo.getFreightId())
            .ownerType("freight")
            .type(PushRefType.SET_URGENT_WAYBILL.getValue())
            .content(PushTemplate.SET_URGENT_WAYBILL_TEMPLATE(freightInfo.getOrderNo(), urgent.getRemark()))
            .build());

        return update > 0;
    }

    /**
     * 取消紧急运单
     *
     * @param urgent
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean cancelUrgentWaybill(AdminUrgentBo urgent) {
        Long id = urgent.getId();
        FreightInfo freightInfo = freightInfoMapper.selectById(id);
        if (freightInfo == null) {
            throw new ServiceException("运单不存在");
        }
        // 运单在紧急状态下才能取消
        if (!SysYesNo.Y.name().equals(freightInfo.getIsUrgency())) {
            throw new ServiceException("运单不在紧急状态下");
        }
        int update = freightInfoMapper.updateUrgent(id, SysYesNo.N.name(), null);

        // 如果运单取消了紧急状态，且全部取消了，则订单也取消紧急状态
        long count = freightInfoMapper.countUrgentByOrderId(freightInfo.getOrderId());
        if (count == 0) {
            orderInfoMapper.updateUrgent(freightInfo.getOrderId(), SysYesNo.N.name(), null);
        }

        return update > 0;
    }

    /**
     * 上传运单回单
     *
     * @param receipt
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean insertReceipt(AdminUploadBillBo receipt) {
        Long id = receipt.getMasterId();
        FreightInfo freightInfo = freightInfoMapper.selectById(id);
        if (freightInfo == null) {
            throw new ServiceException("运单不存在");
        }
        // 只有待回单状态下的运单可以上传回单
        if (!FreightStatus.WAITING_FOR_RECEIPT.getCode().equals(freightInfo.getFreightStatus())) {
            throw new ServiceException("只有在待回单状态下的运单可以上传回单");
        }

        boolean delete = freightReceiptMapper.deleteByReceiptId(receipt.getMasterId());
        FreightReceipt freightReceipt = new FreightReceipt();
        freightReceipt.setFreightId(receipt.getMasterId());
        freightReceipt.setUrl(receipt.getUrl());
        freightReceipt.setRemark(receipt.getRemark());
        int insert = freightReceiptMapper.insert(freightReceipt);

        if (insert > 0) {
            // 修改运单状态为已回单
            freightInfoMapper.updateStatus(id, FreightStatus.WAITING_FOR_PAYMENT.getCode());
            long count = freightInfoMapper.countStatusByOrderId(freightInfo.getOrderId(), FreightStatus.WAITING_FOR_RECEIPT.getCode());
            if (count == 0) {
                // 修改订单状态到待打款
                orderInfoMapper.updateStatus(freightInfo.getOrderId(), OrderStatus.WAITING_FOR_PAYMENT.getCode());
            }
        }
        return insert > 0;
    }

    /**
     * 上传运单打款凭证
     *
     * @param payment
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean insertPayment(AdminUploadBillBo payment) {
        Long id = payment.getMasterId();
        FreightInfo freightInfo = freightInfoMapper.selectById(id);
        if (freightInfo == null) {
            throw new ServiceException("运单不存在");
        }
        // 只有待回单状态下的运单可以上传回单
        if (!FreightStatus.WAITING_FOR_PAYMENT.getCode().equals(freightInfo.getFreightStatus())) {
            throw new ServiceException("只有在待打款状态下的运单可以上传打款凭证");
        }

        boolean delete = paymentVoucherMapper.deleteByFreightId(payment.getMasterId());
        FreightPaymentVoucher freightPaymentVoucher = new FreightPaymentVoucher();
        freightPaymentVoucher.setFreightId(payment.getMasterId());
        freightPaymentVoucher.setUrl(payment.getUrl());
        freightPaymentVoucher.setRemark(payment.getRemark());
        int insert = paymentVoucherMapper.insert(freightPaymentVoucher);

        if (insert > 0) {
            // 修改运单状态为已完成
            freightInfoMapper.updateStatus(id, FreightStatus.COMPLETED.getCode());
        }
        return insert > 0;
    }

    /**
     * 获取运单列表
     *
     * @param freightQueryBo 运单列表查询bo
     * @param pageQuery      分页查询
     * @return
     */
    @Override
    public TableDataInfo<AdminFreightInfoVo> listWaybill(AdminFreightQueryBo freightQueryBo, PageQuery pageQuery) {
        QueryWrapper queryWrapper = new QueryWrapper<>();
        queryWrapper
            .in(freightQueryBo.getFreightStatusList() != null && !freightQueryBo.getFreightStatusList().isEmpty(),
                "f.freight_status", freightQueryBo.getFreightStatusList())
            .like(StrUtil.isNotEmpty(freightQueryBo.getOrderNo()), "f.order_no", freightQueryBo.getOrderNo())
            .eq(StrUtil.isNotEmpty(freightQueryBo.getIsUrgency()), "f.is_urgency", freightQueryBo.getIsUrgency())
            .like(StrUtil.isNotEmpty(freightQueryBo.getCompanyName()), "o.send_company_name", freightQueryBo.getCompanyName())
            .eq(StrUtil.isNotEmpty(freightQueryBo.getCompanyPhone()), "o.send_phone", freightQueryBo.getCompanyPhone())
            .eq(StrUtil.isNotEmpty(freightQueryBo.getCarUseType()), "o.car_use_type", freightQueryBo.getCarUseType())
            .eq(StrUtil.isNotEmpty(freightQueryBo.getDriverType()), "f.driver_type", freightQueryBo.getDriverType())
            .ge(StrUtil.isNotEmpty(freightQueryBo.getOrderStartTime()), "o.create_time", freightQueryBo.getOrderStartTime())
            .le(StrUtil.isNotEmpty(freightQueryBo.getOrderEndTime()), "o.create_time", freightQueryBo.getOrderEndTime())
            .eq("f.is_deleted", "0");

        IPage<AdminFreightInfoVo> result = freightInfoMapper.selectAdminVoList(pageQuery.build(), queryWrapper);

        return TableDataInfo.build(result);
    }

    /**
     * 获取运单改价历史记录
     *
     * @param waybillId 运单id
     * @return
     */
    @Override
    public AdminWaybillRevaluationVo listWaybillPrice(Long waybillId) {
        FreightInfo freightInfo = freightInfoMapper.selectById(waybillId);
        if (freightInfo == null) {
            throw new ServiceException("运单不存在");
        }

        List<FreightRevaluationHistoryVo> revaluationHistoryVoList = freightRevaluationHistoryMapper.selectVoList(new QueryWrapper<FreightRevaluationHistory>()
            .lambda()
            .eq(FreightRevaluationHistory::getFreightId, waybillId)
            .orderByDesc(FreightRevaluationHistory::getCreateTime));

        AdminWaybillRevaluationVo revaluationVo = new AdminWaybillRevaluationVo();
        revaluationVo.setOrderId(freightInfo.getOrderId());
        revaluationVo.setWaybillNo(freightInfo.getCode());
        revaluationVo.setOrderNo(freightInfo.getOrderNo());

        revaluationVo.setHistoryList(revaluationHistoryVoList);


        return revaluationVo;
    }

    /**
     * 获取运单定位历史记录
     *
     * @param waybillId 运单id
     * @return
     */
    @Override
    public AdminWaybillVehicleTrailVo listWaybillVehicleTrail(Long waybillId) {
        FreightInfo freightInfo = freightInfoMapper.selectById(waybillId);
        if (freightInfo == null) {
            throw new ServiceException("运单不存在");
        }

        List<FreightVehicleTrailVo> locationHistoryVoList = freightVehicleTrailMapper.selectVoList(new QueryWrapper<FreightVehicleTrail>()
            .lambda()
            .eq(FreightVehicleTrail::getFreightId, waybillId)
            .orderByDesc(FreightVehicleTrail::getRecordTime));

        AdminWaybillVehicleTrailVo vehicleTrailVo = new AdminWaybillVehicleTrailVo();
        vehicleTrailVo.setOrderId(freightInfo.getOrderId());
        vehicleTrailVo.setWaybillNo(freightInfo.getCode());
        vehicleTrailVo.setOrderNo(freightInfo.getOrderNo());

        vehicleTrailVo.setTrailList(locationHistoryVoList);

        return vehicleTrailVo;
    }
}
